/*
 * Niiuzu
 * Copyright (C) 2011  Thierry Nowak
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.niiuzu.server.util;

import java.util.regex.Pattern;

import com.niiuzu.server.util.exception.WrongBytesStringFormatException;

/**
 * Classe utilitaire d'aide à la manipulation de {@link Byte}.
 */
public final class BytesUtil {

	private static final int BYTE_FILTER = 0x0f;
	private static final int BYTE_SHIFT = 4;

	/** Pattern des chaînes de caractères interprétées. */
	public static final String PATTERN_STRING_BYTES = "([0-9a-f]{2})*";

	private static final char[] CHARSET_HEX = "0123456789abcdef".toCharArray();

	/**
	 * Construit une nouvelle instance de {@link BytesUtil}.
	 */
	private BytesUtil() {
	}

	/**
	 * Convertit un tableau de <tt>byte</tt> en {@link String}.
	 */
	public static String convertBytesToString(byte[] bytes) {
		if (bytes == null) {
			return null;
		}

		StringBuilder builder = new StringBuilder(bytes.length * 2);
		for (byte aByte : bytes) {
			builder.append(CHARSET_HEX[aByte >> BYTE_SHIFT & BYTE_FILTER]);
			builder.append(CHARSET_HEX[aByte & BYTE_FILTER]);
		}

		return builder.toString();
	}

	/**
	 * Convertit une {@link String} en tableau de <tt>byte</tt>.
	 * 
	 * @param encodedBytes
	 *            chaîne de caractères respectant le pattern
	 *            {@link #PATTERN_STRING_BYTES}.
	 * 
	 * @throws WrongBytesStringFormatException
	 *             cette erreur se propage si <tt>encodedBytes</tt>.
	 * 
	 * @see #PATTERN_STRING_BYTES
	 */
	public static byte[] convertStringToBytes(String encodedBytes)
			throws WrongBytesStringFormatException {
		if (encodedBytes == null) {
			return null;
		}

		if (!Pattern.matches(PATTERN_STRING_BYTES, encodedBytes)) {
			throw new WrongBytesStringFormatException();
		}

		char[] bytesAsCharsArray = encodedBytes.toCharArray();

		int bytesLength = encodedBytes.length() / 2;
		byte[] bytes = new byte[bytesLength];
		for (int i = 0; i < bytes.length; ++i) {
			char byteChar1 = bytesAsCharsArray[i * 2];
			char byteChar2 = bytesAsCharsArray[i * 2 + 1];

			int byteIndex1 = getIntValueFor(byteChar1);
			int byteIndex2 = getIntValueFor(byteChar2);

			int byteIntegerValue = byteIndex1 * 16 + byteIndex2;

			bytes[i] = Integer.valueOf(byteIntegerValue).byteValue();
		}

		return bytes;
	}

	/**
	 * Récupère la valeur numérique correspondant à un caractère.
	 */
	private static int getIntValueFor(char aChar) {
		if (aChar < 'a') {
			// [0-9]
			return aChar - '0';
		} else {
			// [a-f]
			return (aChar - 'a') + 10;
		}
	}

}
